package com.lianjia.analysis.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 日志过滤器
 * 用于记录API请求和响应信息
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LoggingFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(LoggingFilter.class);
    
    private static final List<String> BINARY_CONTENT_TYPES = Arrays.asList(
            "image/", "audio/", "video/", "application/octet-stream", 
            "application/pdf", "application/zip"
    );

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
            throws ServletException, IOException {
        
        // 只记录API请求
        if (!request.getRequestURI().startsWith("/api/")) {
            filterChain.doFilter(request, response);
            return;
        }
        
        // 包装请求和响应以便多次读取
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 前置日志
            logRequest(requestWrapper);
            
            // 执行请求
            filterChain.doFilter(requestWrapper, responseWrapper);
            
            // 后置日志
            logResponse(responseWrapper, System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            // 异常日志
            log.error("请求处理异常: {}", request.getRequestURI(), e);
            throw e;
        } finally {
            // 复制响应内容到原始响应
            responseWrapper.copyBodyToResponse();
        }
    }
    
    private void logRequest(ContentCachingRequestWrapper request) {
        String queryString = request.getQueryString() != null ? "?" + request.getQueryString() : "";
        String method = request.getMethod();
        String uri = request.getRequestURI() + queryString;
        
        // 记录基本请求信息
        log.info("收到API请求: {} {}", method, uri);
        
        // 记录请求头
        if (log.isDebugEnabled()) {
            Collections.list(request.getHeaderNames()).forEach(headerName -> {
                Collections.list(request.getHeaders(headerName)).forEach(headerValue -> {
                    log.debug("请求头: {}: {}", headerName, headerValue);
                });
            });
        }
        
        // 记录请求体 (POST, PUT等)
        if (shouldLogRequestBody(request)) {
            String payload = getRequestPayload(request);
            if (payload != null && !payload.isEmpty()) {
                log.debug("请求体: {}", payload);
            }
        }
    }
    
    private void logResponse(ContentCachingResponseWrapper response, long executionTime) {
        int status = response.getStatus();
        
        // 记录基本响应信息
        log.info("API响应: 状态码={}, 耗时={}ms", status, executionTime);
        
        // 记录响应头
        if (log.isDebugEnabled()) {
            response.getHeaderNames().forEach(headerName -> {
                response.getHeaders(headerName).forEach(headerValue -> {
                    log.debug("响应头: {}: {}", headerName, headerValue);
                });
            });
        }
        
        // 记录响应体
        if (shouldLogResponseBody(response)) {
            String payload = getResponsePayload(response);
            if (payload != null && !payload.isEmpty()) {
                // 如果响应体太长，只记录部分
                if (payload.length() > 1000) {
                    log.debug("响应体(截断): {}...", payload.substring(0, 1000));
                } else {
                    log.debug("响应体: {}", payload);
                }
            }
        }
    }
    
    private boolean shouldLogRequestBody(HttpServletRequest request) {
        String contentType = request.getContentType();
        
        // 不记录二进制内容
        if (contentType != null && BINARY_CONTENT_TYPES.stream().anyMatch(contentType::startsWith)) {
            return false;
        }
        
        // 只记录特定请求方法的请求体
        String method = request.getMethod();
        return "POST".equals(method) || "PUT".equals(method) || "PATCH".equals(method);
    }
    
    private boolean shouldLogResponseBody(ContentCachingResponseWrapper response) {
        String contentType = response.getContentType();
        
        // 不记录二进制内容
        if (contentType != null && BINARY_CONTENT_TYPES.stream().anyMatch(contentType::startsWith)) {
            return false;
        }
        
        return true;
    }
    
    private String getRequestPayload(ContentCachingRequestWrapper request) {
        byte[] content = request.getContentAsByteArray();
        if (content.length == 0) {
            return null;
        }
        
        String contentType = request.getContentType();
        String payload = null;
        
        try {
            payload = new String(content, request.getCharacterEncoding());
        } catch (UnsupportedEncodingException e) {
            log.warn("无法解析请求体: {}", e.getMessage());
            payload = "[无法解析的内容]";
        }
        
        return payload;
    }
    
    private String getResponsePayload(ContentCachingResponseWrapper response) {
        byte[] content = response.getContentAsByteArray();
        if (content.length == 0) {
            return null;
        }
        
        String contentType = response.getContentType();
        String payload = null;
        
        try {
            payload = new String(content, response.getCharacterEncoding());
        } catch (UnsupportedEncodingException e) {
            log.warn("无法解析响应体: {}", e.getMessage());
            payload = "[无法解析的内容]";
        }
        
        return payload;
    }
}